﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.LinearEquationSolvers;

namespace SmartMathLibrary.NonlinearEquationSolvers
{
    /// <summary>
    /// This class provides the solving of a complex nonlinear system of equation by using the 
    /// Broyden method in a complex version.
    /// </summary>
    [Serializable]
    public class BroydenComplexNonLinearSystemSolver : AbstractComplexNonLinearSystemSolver
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BroydenComplexNonLinearSystemSolver"/> class.
        /// </summary>
        /// <param name="equationSystem">The complex nonlinear system of equation to solve.</param>
        public BroydenComplexNonLinearSystemSolver(ComplexNonLinearEquationSystem equationSystem)
            : base(equationSystem)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BroydenComplexNonLinearSystemSolver"/> class.
        /// </summary>
        /// <param name="equationSystemFunctions">The complex nonlinear system of equation to solve.</param>
        public BroydenComplexNonLinearSystemSolver(HardMultivariateComplexFunction[] equationSystemFunctions)
            : base(equationSystemFunctions)
        {
        }

        /// <summary>
        /// Solves the complex nonlinear system of equation by using the Broyden method in a complex 
        /// version. The property  PrecisionError have to be checked after the calculation to 
        /// be sure that the results are correct.
        /// </summary>
        /// <param name="startValues">The start values for the calculation.</param>
        /// <param name="iterations">The maximum number of iterations.</param>
        /// <param name="precision">The precision of the result.</param>
        /// <returns>
        /// The approximated numeric results, which solves the complex nonlinear system of equation.
        /// </returns>
        public override GeneralComplexVector SolveNonLinearSystem(GeneralComplexVector startValues, int iterations,
                                                                  double precision)
        {
            if (startValues == (GeneralComplexVector) null)
            {
                throw new ArgumentNullException("startValues");
            }

            if (startValues.Count != this.EquationSystem.EquationSystem.Length)
            {
                throw new ArgumentException(
                    "The number of components in the start vector has to be even with the number of equations.");
            }

            if (this.EquationSystem.EquationSystem.Length < 2)
            {
                throw new ArgumentException(
                    "The system of equation has to have to contain a minimum number of two equations.");
            }

            int iterationCount = 0;
            GeneralComplexVector result = startValues.Copy();
            GeneralComplexVector x1 = new GeneralComplexVector(this.EquationSystem.EquationSystem.Length);
            ComplexMatrix matrix = new ComplexMatrix(this.EquationSystem.EquationSystem.Length,
                                                     this.EquationSystem.EquationSystem.Length);

            for (int i = 0; i < this.EquationSystem.EquationSystem.Length; i++)
            {
                for (int j = 0; j < this.EquationSystem.EquationSystem.Length; j++)
                {
                    matrix.SetValueAtPosition(i, j, new ComplexNumber(0));
                }

                matrix.SetValueAtPosition(i, i, new ComplexNumber(1));
            }

            while (iterationCount < iterations)
            {
                ComplexNumber delta = new ComplexNumber(0);
                GeneralComplexVector functionResult1 = this.EquationSystem.SolveAt(result);
                GaussComplexLinearSystemSolver linearSystemSolver = new GaussComplexLinearSystemSolver(matrix,
                                                                                                       functionResult1);
                GeneralComplexVector s = linearSystemSolver.SolveLinearSystem();

                for (int i = 0; i < this.EquationSystem.EquationSystem.Length; i++)
                {
                    x1[i] = result[i] - s[i];
                    delta += s[i] * s[i];
                }

                if (Math.Abs(delta.RealNumberPart) <= Math.Abs(precision))
                {
                    break;
                }

                GeneralComplexVector functionResult2 = this.EquationSystem.SolveAt(x1);

                for (int i = 0; i < this.EquationSystem.EquationSystem.Length; i++)
                {
                    ComplexNumber tempuri = functionResult2[i] + functionResult1[i];

                    for (int j = 0; j < this.EquationSystem.EquationSystem.Length; j++)
                    {
                        tempuri -= matrix.GetValueAtPosition(i, j) * s[j];
                    }

                    for (int j = 0; j < this.EquationSystem.EquationSystem.Length; j++)
                    {
                        matrix.SetValueAtPosition(i, j, matrix.GetValueAtPosition(i, j) - tempuri * s[j] / delta);
                    }
                }

                for (int i = 0; i < this.EquationSystem.EquationSystem.Length; i++)
                {
                    functionResult1[i] = functionResult2[i];
                    result[i] = x1[i];
                }

                iterationCount++;
            }

            this.neededIterations = iterationCount;
            this.precisionError = (iterations == iterationCount);

            return result;
        }
    }
}